package com.vdg.lockphotos.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Point;
import android.media.ExifInterface;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import android.widget.ImageView;

import com.vdg.lockphotos.R;
import com.vdg.lockphotos.notify.OnLoadFileListner;

public class ImageLoader {

	MemoryCache memoryCache = new MemoryCache();
	FileCache fileCache;
	private Map<ImageView, String> imageViews = Collections
			.synchronizedMap(new WeakHashMap<ImageView, String>());
	ExecutorService executorService;
	private final static String TAG = "ImageLoader";
	Handler handler = new Handler();// handler to display images in UI thread
	private Context mContext;
	private static ImageLoader sInstance;

	public ImageLoader(Context context) {
		fileCache = new FileCache(context);
		executorService = Executors.newFixedThreadPool(1);
		mContext = context;
	}

	public byte[] readBytes(InputStream inputStream) throws IOException {
		// this dynamically extends to take the bytes you read
		ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();

		// this is storage overwritten on each iteration with bytes
		int bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];

		// we need to know how may bytes were read to write them to the
		// byteBuffer
		int len = 0;
		while ((len = inputStream.read(buffer)) != -1) {
			byteBuffer.write(buffer, 0, len);
		}

		// and then we can return your byte array.
		return byteBuffer.toByteArray();
	}

	public static ImageLoader getInstance(Context context) {
		if (sInstance == null) {
			sInstance = new ImageLoader(context);
		}
		return sInstance;
	}

	final int stub_id = R.drawable.stub;

	public synchronized void DisplayImage(String url, ImageView imageView,
			OnLoadFileListner listener, float size, boolean isThumbnail) {
		if (listener != null) {
			listener.onStart();
		}

		Log.v(TAG, "bitmap not null0 " + url);
		imageViews.put(imageView, url);
		Bitmap bitmap;
		if (isThumbnail) {
			bitmap = memoryCache.get("thumbnail_" + url);
		} else {
			bitmap = memoryCache.get(url);

		}

		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
			if (listener != null) {
				listener.onSuccess(false);
			}
			Log.v(TAG, "bitmap not null1");
		} else {
			Log.v(TAG, "bitmap null1 " + url);
			queuePhoto(url, imageView, listener, size, isThumbnail);
			// imageView.setImageResource(stub_id);
		}
	}

	public boolean checkBitmapConstain(String url, boolean isThumbnail) {
		Bitmap bitmap;
		if (isThumbnail) {
			bitmap = memoryCache.get("thumbnail_" + url);
		} else {
			bitmap = memoryCache.get(url);

		}
		if (bitmap != null) {
			return true;
		}
		return false;
	}

	private void queuePhoto(String url, ImageView imageView,
			OnLoadFileListner listener, float size, boolean isThumbnai) {
		PhotoToLoad p = new PhotoToLoad(url, imageView, size);
		executorService.submit(new PhotosLoader(p, listener, isThumbnai));
	}

	private synchronized Bitmap getBitmap(String url, float size) {
		File f = fileCache.getFile(url);
		try {
			// from SD cache
			Bitmap b = resizeAndRotateBitmap(f, decodeFile(f, mContext, size),
					mContext);
			if (b != null) {
				Log.v(TAG, "bitmap not null");
				return b;
			}
			Log.v(TAG, "bitmap null");
			// from web
			// Bitmap bitmap = null;
			// Log.v()

			HttpGet httpRequest = null;
			httpRequest = new HttpGet(new URL(url).toURI());
			HttpClient httpclient = new DefaultHttpClient();
			HttpResponse response = (HttpResponse) httpclient
					.execute(httpRequest);

			HttpEntity entity = response.getEntity();
			BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
			InputStream instream = bufHttpEntity.getContent();

			return resizeAndRotateBitmap(null,
					BitmapFactory.decodeStream(instream), mContext);
			// URL imageUrl = new URL(url);
			// HttpURLConnection conn = (HttpURLConnection) imageUrl
			// .openConnection();
			// conn.setConnectTimeout(30000);
			// conn.setReadTimeout(30000);
			// conn.setInstanceFollowRedirects(true);
			// InputStream is = conn.getInputStream();
			// OutputStream os = new FileOutputStream(f);
			// Utils.CopyStream(is, os);
			// os.close();
			// conn.disconnect();
			// bitmap = resizeAndRotateBitmap(decodeFile(f, mContext),
			// mContext);
			// return bitmap;
		} catch (Throwable ex) {
			ex.printStackTrace();
			if (ex instanceof OutOfMemoryError) {
				memoryCache.clear();
				try {
					return resizeAndRotateBitmap(f,
							decodeFile(f, mContext, size), mContext);
				} catch (Exception e) {
					// TODO: handle exception
					return null;
				}
			}
			return null;
		}
	}

	public static int calculateInSampleSize(BitmapFactory.Options options,
			int reqWidth, int reqHeight) {
		// Raw height and width of image
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth) {

			// Calculate ratios of height and width to requested height and
			// width
			final int heightRatio = Math.round((float) height
					/ (float) reqHeight);
			final int widthRatio = Math.round((float) width / (float) reqWidth);

			// Choose the smallest ratio as inSampleSize value, this will
			// guarantee
			// a final image with both dimensions larger than or equal to the
			// requested height and width.
			inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
		}

		return inSampleSize;
	}

	public static synchronized Bitmap decodeFile(File f, Context activity,
			float size) {
		try {
			// Decode image size
			BitmapFactory.Options o = new BitmapFactory.Options();
			o.inJustDecodeBounds = true;
			BitmapFactory.decodeStream(new FileInputStream(f), null, o);
			BitmapFactory.Options o2 = new BitmapFactory.Options();
			if (size != 0) {
				o2.inSampleSize = calculateInSampleSize(o, (int) size,
						(int) size);
			}
			return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@SuppressLint("NewApi")
	private static Point getDisplaySize(final Display display) {
		final Point point = new Point();
		try {
			display.getSize(point);
		} catch (java.lang.NoSuchMethodError ignore) { // Older device
			point.x = display.getWidth();
			point.y = display.getHeight();
		}
		return point;
	}

	public static synchronized Bitmap resizeAndRotateBitmap(File f, Bitmap m,
			Context activity) {
		if (m == null) {
			return null;
		}
		if (m.getWidth() >= m.getHeight()) {
			// int targetWidth = activity.getResources().getDimensionPixelSize(
			// R.dimen.chat_image_size);
			Matrix mt = new Matrix();
			if (f != null) {
				ExifInterface exif = null;
				try {
					exif = new ExifInterface(f.getAbsolutePath());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				int orientation = exif.getAttributeInt(
						ExifInterface.TAG_ORIENTATION, 1);
				Log.d("EXIF", "Exif: " + orientation);
				if (orientation == 6) {
					mt.postRotate(90);
				} else if (orientation == 3) {
					mt.postRotate(180);
				} else if (orientation == 8) {
					mt.postRotate(270);
				}
			}
			return Bitmap.createBitmap(m, 0, 0, m.getWidth(), m.getHeight(),
					mt, true);
		} else {
			// int height = activity.getResources().getDimensionPixelSize(
			// R.dimen.chat_image_size);
			Matrix mt = new Matrix();
			if (f != null) {
				ExifInterface exif = null;
				try {
					exif = new ExifInterface(f.getAbsolutePath());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				int orientation = exif.getAttributeInt(
						ExifInterface.TAG_ORIENTATION, 1);
				Log.d("EXIF", "Exif: " + orientation);
				if (orientation == 6) {
					mt.postRotate(90);
				} else if (orientation == 3) {
					mt.postRotate(180);
				} else if (orientation == 8) {
					mt.postRotate(270);
				}
			}
			return Bitmap.createBitmap(m, 0, 0, m.getWidth(), m.getHeight(),
					mt, true);
		}
	}

	// Task for the queue
	private class PhotoToLoad {
		public String url;
		public ImageView imageView;
		private float size;

		public PhotoToLoad(String u, ImageView i, float size) {
			url = u;
			imageView = i;
			this.size = size;
		}
	}

	class PhotosLoader implements Runnable {
		PhotoToLoad photoToLoad;
		OnLoadFileListner mOnLoadFileListner;
		private boolean isThumbnail;

		PhotosLoader(PhotoToLoad photoToLoad, OnLoadFileListner listner,
				boolean isThumbnail) {
			this.photoToLoad = photoToLoad;
			this.mOnLoadFileListner = listner;
			this.isThumbnail = isThumbnail;
		}

		@Override
		public synchronized void run() {
			try {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (imageViewReused(photoToLoad)) {
					if (mOnLoadFileListner != null) {
						this.mOnLoadFileListner.onSuccess(false);
					}
					Log.v(TAG, "bitmap null2");
					return;
				}
				Bitmap bmp = getBitmap(photoToLoad.url, photoToLoad.size);
				if (bmp == null) {
					if (mOnLoadFileListner != null) {
						this.mOnLoadFileListner.onFailed();
						Log.v(TAG, "load image failed :" + photoToLoad.url);
					}
					Log.v(TAG, "bitmap null");
				} else {
					Log.v(TAG, "bitmap not null");
				}
				if (isThumbnail) {
					memoryCache.put("thumbnail_" + photoToLoad.url, bmp);
				} else {
					memoryCache.put(photoToLoad.url, bmp);
				}
				if (imageViewReused(photoToLoad)) {
					if (mOnLoadFileListner != null) {
						this.mOnLoadFileListner.onSuccess(false);
					}
					return;
				}
				BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad,
						mOnLoadFileListner);
				handler.post(bd);
			} catch (Throwable th) {
				th.printStackTrace();
				if (mOnLoadFileListner != null) {
					this.mOnLoadFileListner.onFailed();
				}
			}
		}
	}

	boolean imageViewReused(PhotoToLoad photoToLoad) {
		String tag = imageViews.get(photoToLoad.imageView);
		Log.v(TAG, " null  = " + tag);
		if (tag == null || !tag.equals(photoToLoad.url))
			return true;
		return false;
	}

	// Used to display bitmap in the UI thread
	class BitmapDisplayer implements Runnable {
		Bitmap bitmap;
		PhotoToLoad photoToLoad;
		OnLoadFileListner mOnLoadFileListner;

		public BitmapDisplayer(Bitmap b, PhotoToLoad p,
				OnLoadFileListner listener) {
			bitmap = b;
			photoToLoad = p;
			mOnLoadFileListner = listener;
		}

		public void run() {
			if (imageViewReused(photoToLoad)) {
				if (mOnLoadFileListner != null) {
					mOnLoadFileListner.onSuccess(false);
				}
				return;
			}
			if (bitmap != null) {
				photoToLoad.imageView.setImageBitmap(bitmap);
				if (mOnLoadFileListner != null) {
					mOnLoadFileListner.onSuccess(true);
				}
			} else {
				photoToLoad.imageView.setImageResource(stub_id);
				if (mOnLoadFileListner != null) {
					mOnLoadFileListner.onSuccess(true);
				}
			}
		}
	}

	public void clearCache() {
		memoryCache.clear();
		fileCache.clear();
	}

}
